home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 14 / CU Amiga Magazine's Super CD-ROM 14 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-09].iso / CUCD / Online / News / Thor / rexx / SortMail.br < prev    next >
Encoding:
Text File  |  1996-11-11  |  57.3 KB  |  1,833 lines

  1. /*
  2. ** $VER: SortMail.br 3.34 (27.10.96)
  3. ** by Eirik Nicolai Synnes
  4. **
  5. ** Some code borrowed from AddSOUP.thor     by Magne Østlyngen
  6. **                     and AddAmiNetList.br by Petter Nilsen
  7. **
  8. ** See SortMail.guide for documentation
  9. **
  10. */
  11.  
  12. options results
  13. options failat 31
  14.  
  15. /*signal on error*/
  16. signal on syntax
  17. signal on break_c
  18. signal on halt
  19.  
  20. parse arg arguments
  21.  
  22. /*
  23. ** Initialize some variables
  24. */
  25.  
  26. version         = subword(sourceline(2), 4)
  27.  
  28. cfgfile         = 'SortMail.cfg'
  29.  
  30. template        = 'SYSTEM/A,MSGNO/K/N/M,SHANGHAI/S,LOGINSTATE/S,NOWARN/S,NOCOUNTER/S,ALL/S,QUIET/S'
  31.  
  32. args.all        = 0; args.quiet      = 0; args.shanghai    = 0;
  33. args.loginstate = 0; args.nowarn     = 0; args.nocounter   = 0;
  34.  
  35. fromthor        = 0; delnewfiles     = 0
  36. logcount        = 0; aminetlogcount  = 0; errlogcount      = 0
  37.  
  38. BDB_ADD_USERS           =  9  /* Parser should add users to database. */
  39.  
  40. MDB_READ                =  1  /* Message is read. */
  41. MDB_DELETED             =  5  /* Message is deleted. */
  42. MDB_MARKED              = 10  /* Message is marked. */
  43. MDB_SUPERMARKED         = 13  /* Message will not be unmarked as long as this flag is set. */
  44.  
  45. UDB_DELETED             =  0  /* User is deleted */
  46. UDB_UNRECOVERABLE       =  1  /* User can not be undeleted */
  47.  
  48. globals = 'sigl thorport ver. fromthor progwin thorpath cfgfile log. logcount delnewfiles aminetlog. aminetlogcount errlog. errlogcount counter data. head. text. textread globalcfg. args. trigger. newmsg. bbsdata. conflist. cursys. fileopen BBSREAD.LASTERROR THOR.LASTERROR globals'
  49.  
  50. /* See if I'm run from Thor */
  51.  
  52. if left(address(), 5) = 'THOR.' then do
  53.     thorport = address()
  54.  
  55.     address(thorport)
  56.     'GETGLOBALCONFIG STEM 'globcfg
  57.     if rc ~= 0 then call displayerror(30, 'SortMail', 'GETGLOBALCONFIG: 'THOR.LASTERROR)
  58.  
  59.     pubscreen = globcfg.PUBSCREENNAME
  60.     fromthor = 1
  61.     end
  62.  
  63. /* Find/open BBSREAD ARexx port */
  64.  
  65. if ~show('P', 'BBSREAD') then do
  66.     address(command)
  67.     'Run >NIL: `GetEnv THOR/THORPath`bin/LoadBBSRead'
  68.     'WaitForPort BBSREAD'
  69.     if rc ~= 0 then displayerror(30, 'SortMail', 'Couldn''t open BBSREAD''s ARexx port.')
  70.     end
  71.  
  72. /*
  73. ** Give template if arguments = '?'
  74. */
  75.  
  76. if arguments = '?' then do
  77.     say 'Usage: 'template
  78.     signal cleanup
  79.     end
  80.  
  81. /*
  82. ** See if another copy of SortMail is already running
  83. */
  84.  
  85. if getclip('SM_Active') ~= '' then call notify("Another copy of SortMail is probably running.\nDo you want to continue?", "Yes|No")
  86. if result = 0 then exit(0)
  87. call setclip('SM_Active', 'True')
  88.  
  89. /*
  90. ** See if user has entered a system or is in the startup window
  91. */
  92.  
  93. if fromthor then do
  94.     address(thorport)
  95.     'CURRENTSYSTEM STEM 'cursys
  96.     if rc = 1 then do
  97.         call displayerror(30, 'SortMail', 'Enter a system before running this script.')
  98.         end
  99.     else if rc > 1 then call displayerror(30, 'SortMail', 'CURRENTSYSTEM: 'THOR.LASTERROR)
  100.  
  101.     address(bbsread)
  102.     'GETBBSDATA "'cursys.BBSNAME'" 'bbsdata
  103.     if rc ~= 0 then call displayerror(30, 'SortMail', 'GETBBSDATA: 'BBSREAD.LASTERROR)
  104.     end
  105. else do
  106.     address(bbsread)
  107.     'READARGS "'template'" 'args' CMDLINE 'arguments
  108.     if rc ~= 0 then do
  109.         say BBSREAD.LASTERROR
  110.         say 'Template: 'template
  111.         signal cleanup
  112.         end
  113.  
  114.     /* Check validity of command line arguments */
  115.     if args.SHANGHAI & symbol('args.MSGNO.COUNT') = 'VAR' then call displayerror(30, 'SortMail', 'You can''t specify both SHANGHAI/S and MSGNO/K/N at the same time.')
  116.     if args.LOGINSTATE & ~args.SHANGHAI then call displayerror(30, 'SortMail', 'LOGINSTATE/S can only be used together with SHANGHAI/S.')
  117.  
  118.     globalcfg.SYSTEM = args.SYSTEM
  119.  
  120.     address(bbsread)
  121.     'GETBBSDATA "'globalcfg.SYSTEM'" 'bbsdata
  122.     if rc ~= 0 then call displayerror(30, 'SortMail', 'GETBBSDATA: 'BBSREAD.LASTERROR)
  123.     end
  124.  
  125. /*
  126. ** Find the configuration file
  127. */
  128.  
  129. if ~exists(bbsdata.BBSPATH || cfgfile) then call displayerror(30, 'SortMail', 'Couldn''t find configuration file ('bbsdata.BBSPATH || cfgfile').')
  130.  
  131. /*
  132. ** Display some progress info
  133. */
  134.  
  135. if fromthor then do
  136.     address(thorport)
  137.     'OPENPROGRESS TITLE "SortMail.br 'version'" PT "Reading configuration..." AT "_Abort" PCW 40'
  138.     if rc = 0 then progwin = result
  139.     else call displayerror(30, 'SortMail', 'OPENPROGRESS: 'THOR.LASTERROR)
  140.     end
  141. else if ~args.QUIET then do
  142.     say 'SortMail 'version' by Eirik Nicolai Synnes'
  143.     say 'Reading configuration...'
  144.   end
  145.  
  146. /*
  147. ** Read configuration
  148. */
  149.  
  150. call readcfg()
  151.  
  152. if args.NOWARN then globalcfg.NOWARN = 1
  153.  
  154. /*
  155. ** Get some system info
  156. */
  157.  
  158. if ~fromthor then do
  159.     'GETCONFDATA "'globalcfg.SYSTEM'" "'globalcfg.conference'" STEM 'confdata
  160.     if rc ~= 0 then call displayerror(30, 'SortMail', 'GETCONFDATA: 'BBSREAD.LASTERROR)
  161.     end
  162.  
  163. /*
  164. ** Exit if there's no marked messages in email conference
  165. */
  166.  
  167. if args.LOGINSTATE & confdata.MSGMARKED = 0 then signal cleanup
  168.  
  169. /*
  170. ** Get a list of the messages to process
  171. */
  172.  
  173. if fromthor then do
  174.     /* Get message array from Thor */
  175.  
  176.     if globalcfg.LOGINSTATE then lstate = 'LOGINSTATE'
  177.     else lstate = ''
  178.     address(thorport)
  179.     'GETMESSAGEARRAY "'globalcfg.conference'" 'msgs' 'lstate
  180.     if rc = 5 then signal cleanup
  181.     else if rc ~= 0 then call displayerror(30, 'SortMail', 'GETMESSAGEARRAY: 'THOR.LASTERROR)
  182.     end
  183. else do
  184.     select
  185.         when symbol('args.MSGNO.COUNT') = 'VAR' then do
  186.             /* Create message array from msg number given on cmd line */
  187.  
  188.             argcnt = 0
  189.             do i = 1 to args.MSGNO.COUNT
  190.                 if (args.MSGNO.i > confdata.FIRSTMSG - 1) & (args.MSGNO.i < confdata.LASTMSG + 1) then do
  191.                     argcnt = argcnt + 1
  192.                     msgs.argcnt = args.MSGNO.i
  193.                     end
  194.                 end
  195.  
  196.             msgs.count = argcnt
  197.             args.ALL = 1
  198.             end
  199.  
  200.         when args.SHANGHAI then do
  201.             /* Get message array from an available Thor port */
  202.  
  203.             ports = show('P')
  204.             do i = 1 to words(ports)
  205.                 if pos(' THOR.', ports) > 0 then thorport = word(substr(ports, pos(' THOR.', ports)), 1)
  206.                 end
  207.  
  208.             if thorport ~= 'THORPORT' then do
  209.                 if args.LOGINSTATE then lstate = 'LOGINSTATE'; else lstate = ''
  210.  
  211.                 call getver()
  212.                 address(thorport)
  213.  
  214.                 if (ver.thorver > 2) | ((ver.thorver = 2) & (ver.thorrev > 31)) then do
  215.                     'GETMESSAGEARRAY CONFNAME "'globalcfg.conference'" SYSTEM "'globalcfg.system'" STEM 'msgs lstate
  216.                     if rc = 5 then signal cleanup
  217.                     else if rc ~= 0 then call displayerror(30, 'SortMail', 'GETMESSAGEARRAY: 'THOR.LASTERROR)
  218.                     end
  219.                 else do
  220.                     'CURRENTSYSTEM STEM 'cursys
  221.                     if rc > 1 then displayerror(rc, 'SortMail', 'CURRENTSYSTEM: 'THOR.LASTERROR)
  222.                     else if rc = 0 then do
  223.                         if cursys.BBSNAME = globalcfg.SYSTEM then do
  224.                             'GETMESSAGEARRAY CONFNAME "'globalcfg.conference'" STEM 'msgs lstate
  225.                             if rc = 5 then signal cleanup
  226.                             else if rc ~= 0 then call displayerror(30, 'SortMail', 'GETMESSAGEARRAY: 'THOR.LASTERROR)
  227.                             end
  228.                         end
  229.                     end
  230.                 args.ALL = 1
  231.                 end
  232.             else displayerror(30, 'SortMail', 'Couldn''t find Thor''s ARexx port.')
  233.             end
  234.  
  235.         otherwise nop
  236.         end
  237.     end
  238.  
  239. if symbol('msgs.count') ~= 'VAR' then do
  240.     /* Just do 'em all */
  241.  
  242.     if ~args.NOCOUNTER & exists(bbsdata.BBSPATH || 'SortMail.count') then do
  243.         call open(cn, bbsdata.BBSPATH || 'SortMail.count', 'R')
  244.         counter = readln(cn)
  245.         call close(cn)
  246.         if counter > confdata.FIRSTMSG then confdata.FIRSTMSG = counter
  247.         if counter >= confdata.LASTMSG then signal cleanup
  248.         end
  249.  
  250.     msgs.count = confdata.LASTMSG - confdata.FIRSTMSG + 1
  251.  
  252.     if msgs.count > 0 then do i = 1 to (confdata.LASTMSG - confdata.FIRSTMSG) + 1
  253.         msgs.i = confdata.FIRSTMSG + (i - 1)
  254.         end
  255.     end
  256.  
  257. /*
  258. ** Exit if there are no messages to process
  259. */
  260.  
  261. if msgs.count = 0 then signal cleanup
  262.  
  263. /*
  264. ** Set ARexx clips
  265. */
  266.  
  267. call setclip('SM_System', globalcfg.SYSTEM)
  268. call setclip('SM_Conference', globalcfg.CONFERENCE)
  269. if fromthor then setclip('SM_ThorPort', thorport)
  270.  
  271. /*
  272. ** Utilize BBSRead's copyback buffer
  273. */
  274.  
  275. address(bbsread)
  276. 'BUFMODE COPYBACK'
  277.  
  278. /*
  279. ** Start processing messages
  280. */
  281.  
  282. processed = 0; totfound = 0; totfail = 0
  283.  
  284. do i = 1 to msgs.count
  285.     msgfini = 0; textread = 0; failed = 0; dodelmsg = 0; dodeluser = 0
  286.     drop data. head. text.
  287.  
  288.     /* Update progressreport */
  289.     if fromthor then do
  290.         progtext = 'Processing message 'i' of 'msgs.count' (#'msgs.i')'
  291.         address(thorport)
  292.         'UPDATEPROGRESS REQ 'progwin' TOTAL 'msgs.count' CURRENT 'i' PT "'progtext'"'
  293.         if rc = 5 then signal writelog
  294.         else if rc > 0 then call displayerror(rc, 'SortMail', 'UPDATEPROGRESS: 'THOR.LASTERROR, msgs.i)
  295.         end
  296.     else if (~args.QUIET) then do
  297.         say '1B'x'[1A' || '1B'x'[K' || 'Message 'msgs.i' ('i' of 'msgs.count')' || '1B'x'[0m'
  298.         say '1B'x'[1A' || '1B'x'[32CType: '
  299.         end
  300.  
  301.     processed = processed + 1
  302.  
  303.     /* Read message data */
  304.     address(bbsread)
  305.     'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.conference'" 'msgs.i' DATASTEM 'data
  306.     if rc ~= 0 then do
  307.         call displayerror(10, 'SortMail', 'READBRMESSAGE, data stem: 'BBSREAD.LASTERROR, msgs.i)
  308.         iterate i
  309.         end
  310.  
  311.     counter = msgs.i
  312.  
  313.     /* If messsage is marked as deleted or superunread then skip it */
  314.     if ~fromthor & ~bittst(data.FLAGS, MDB_MARKED) & ~args.all then msgfini = 1
  315.     if bittst(data.FLAGS, MDB_SUPERMARKED) then msgfini = 1
  316.     if bittst(data.FLAGS, MDB_DELETED)     then msgfini = 1
  317.     if msgfini = 1 then iterate i
  318.  
  319.     'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.conference'" 'msgs.i' HEADSTEM 'head
  320.     if rc ~= 0 then do
  321.         call displayerror(10, 'SortMail', 'READBRMESSAGE, head stem: 'BBSREAD.LASTERROR, msgs.i)
  322.         iterate i
  323.         end
  324.  
  325.     /* Trigger loop */
  326.     address(bbsread)
  327.     do j = 1 to trigger.count
  328.         foundmsg = 0; foundcrits = 0
  329.  
  330.         /* Search loop */
  331.         do k = 1 to trigger.j.search.count while foundmsg = 0
  332.             /* Search in names, addresses and subject */
  333.             select
  334.                 when trigger.j.search.k.type = 'FROMADDR' then if index(upper(head.FROMADDR), upper(trigger.j.search.k.criteria)) > 0 then foundmsg = 1
  335.                 when trigger.j.search.k.type = 'FROMNAME' then if index(upper(head.FROMNAME), upper(trigger.j.search.k.criteria)) > 0 then foundmsg = 1
  336.                 when trigger.j.search.k.type = 'TOADDR'   then if index(upper(head.TOADDR),   upper(trigger.j.search.k.criteria)) > 0 then foundmsg = 1
  337.                 when trigger.j.search.k.type = 'TONAME'   then if index(upper(head.TONAME),   upper(trigger.j.search.k.criteria)) > 0 then foundmsg = 1
  338.                 when trigger.j.search.k.type = 'SUBJECT'  then if index(upper(head.SUBJECT),  upper(trigger.j.search.k.criteria)) > 0 then foundmsg = 1
  339.                 otherwise if trigger.j.search.k.type ~= 'HEADER' then do
  340.                     if symbol('trigger.j.search.k.type') = 'VAR' then call displayerror(5, 'SortMail', 'Unsupported SEARCH type: 'trigger.j.search.type, msgs.i)
  341.                     else call displayerror(5, 'SortMail', 'Trigger contains invalid search entry', msgs.i)
  342.                     end
  343.                 end
  344.             if trigger.j.matchall & foundmsg then do; foundcrits = foundcrits + 1; foundmsg = 0; end
  345.             end
  346.  
  347.         do k = 1 to trigger.j.search.count while foundmsg = 0
  348.             /* Search in header */
  349.             if trigger.j.search.k.type = 'HEADER' then do
  350.                 if textread = 0 then do
  351.                     'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.conference'" 'msgs.i' TEXTSTEM 'text
  352.                     if rc ~= 0 then do
  353.                         call displayerror(10, 'SortMail', 'READBRMESSAGE, text stem: 'BBSREAD.LASTERROR, msgs.i)
  354.                         iterate i
  355.                         end
  356.                     textread = 1
  357.                     end
  358.                 if symbol('text.COMMENT.COUNT') = 'VAR' & text.COMMENT.COUNT > 0 then do l = 1 to text.COMMENT.COUNT
  359.                     if upper(left(text.COMMENT.l, length(trigger.j.search.k.keyword))) = upper(trigger.j.search.k.keyword) & index(upper(text.COMMENT.l), upper(trigger.j.search.k.criteria)) > 0 then foundmsg = 1
  360.                     end
  361.                 end
  362.             if trigger.j.matchall & foundmsg then do; foundcrits = foundcrits + 1; foundmsg = 0; end
  363.             end
  364.  
  365.         if trigger.j.matchall & foundcrits = trigger.j.search.count then foundmsg = 1
  366.  
  367.         if foundmsg = 0 then iterate j
  368.  
  369.         totfound = totfound + 1
  370.  
  371.         if ~fromthor & ~args.QUIET then do
  372.             say '1B'x'[1A' || '1B'x'[K' || 'Message 'msgs.i' ('i' of 'msgs.count')' || '1B'x'[0m'
  373.             say '1B'x'[1A' || '1B'x'[32CType: 'trigger.j.name
  374.             end
  375.  
  376.         trigger.j.hitcount = trigger.j.hitcount + 1
  377.  
  378.         /* Set ARexx msgno clip */
  379.  
  380.         call setclip('SM_MsgNo', msgs.i)
  381.  
  382.         /* Action loop */
  383.         if trigger.j.action.count > 0 then do k = 1 to trigger.j.action.count while failed = 0
  384.             /* Execute internal functions */
  385.             returned = 0
  386.  
  387.             select
  388.                 when trigger.j.action.k.type = 'COPY' then do
  389.                     call copymsg(globalcfg.SYSTEM, globalcfg.conference, msgs.i, trigger.j.action.k.destconf, trigger.j.action.k.replyaddr, trigger.j.action.k.destsys)
  390.                     returned = result
  391.                     trigger.j.action.k.hitcount = trigger.j.action.k.hitcount + 1
  392.                     end
  393.  
  394.                 when trigger.j.action.k.type = 'RECENT' then do
  395.                     call parseaminet(globalcfg.SYSTEM, globalcfg.conference, msgs.i, trigger.j.action.k.checkdupes, trigger.j.action.k.dontadd, trigger.j.action.k.nostats)
  396.                     returned = result
  397.                     trigger.j.action.k.hitcount = trigger.j.action.k.hitcount + 1
  398.                     end
  399.  
  400.                 when trigger.j.action.k.type = 'SAVEMESSAGE' then do
  401.                     if symbol('trigger.j.action.k.substitute') = 'VAR' then do; subst = trigger.j.action.k.substitute; with =  trigger.j.action.k.with; end
  402.                     else do; subst = ''; with = ''; end
  403.  
  404.                     if symbol('trigger.j.action.k.directory') = 'VAR' then call savemessage(msgs.i, 1, trigger.j.action.k.directory, trigger.j.action.k.header, trigger.j.action.k.append, trigger.j.action.k.nobin, subst, with)
  405.                     else call savemessage(msgs.i, 0, trigger.j.action.k.filename, trigger.j.action.k.header, trigger.j.action.k.append, trigger.j.action.k.nobin, subst, with)
  406.                     returned = result
  407.                     trigger.j.action.k.hitcount = trigger.j.action.k.hitcount + 1
  408.                     drop subst with
  409.                     end
  410.  
  411.                 when trigger.j.action.k.type = 'SPLITDIGEST' then do
  412.                     call splitdigest(msgs.i, trigger.j.action.k.destconf, trigger.j.action.k.replyaddr, trigger.j.action.k.destsys, globalcfg.conference)
  413.                     returned = result
  414.                     trigger.j.action.k.hitcount = trigger.j.action.k.hitcount + 1
  415.                     end
  416.  
  417.                 otherwise if trigger.j.action.k.type ~= 'EXTERNAL' then do
  418.                     returned = 5
  419.                     if symbol('trigger.j.action.type') = 'VAR' then call displayerror(returned, 'SortMail', 'Unsupported ACTION type: 'trigger.j.action.type, msgs.i)
  420.                     else call displayerror(returned, 'SortMail', 'Trigger contains invalid action entry', msgs.i)
  421.                     end
  422.                 end
  423.  
  424.             if returned ~= 0 then do
  425.                 trigger.j.failcount = trigger.j.failcount + 1; failed = 1; totfail = totfail + 1
  426.                 end
  427.             else do
  428.                 if trigger.j.delmsg = 1 then dodelmsg = 1
  429.                 if trigger.j.deluser = 1 then dodeluser = 1
  430.                 end
  431.             end
  432.  
  433.         if trigger.j.action.count > 0 then do k = 1 to trigger.j.action.count while failed = 0
  434.             /* Execute external scripts */
  435.             returned = 0
  436.             if trigger.j.action.k.type = 'EXTERNAL' then do
  437.                 call runexternal(trigger.j.action.k.scriptname, trigger.j.action.k.scriptopts, msgs.i)
  438.                 returned = result
  439.                 trigger.j.action.k.hitcount = trigger.j.action.k.hitcount + 1
  440.  
  441.                 if returned ~= 0 then do
  442.                     trigger.j.failcount = trigger.j.failcount + 1; failed = 1; totfail = totfail + 1
  443.                     end
  444.                 else do
  445.                     if trigger.j.delmsg = 1 then dodelmsg = 1
  446.                     if trigger.j.deluser = 1 then dodeluser = 1
  447.                     end
  448.                 end
  449.             end
  450.  
  451.         leave
  452.         end
  453.  
  454.     /* Remove ARexx msgno clip */
  455.  
  456.     call setclip('SM_MsgNo')
  457.  
  458.     if failed ~= 0 then iterate i
  459.  
  460.     address(bbsread)
  461.  
  462.     /* Delete user? */
  463.     if (dodeluser = 1) & (bittst(bbsdata.FLAGS, BDB_ADD_USERS)) then do
  464.         drop suser.
  465.         'SEARCHBRUSER BBSNAME "'globalcfg.SYSTEM'" STEM 'suser' SEARCH "'head.FROMADDR'" ADDRESS'
  466.         if rc ~= 0 then call displayerror(10, 'SortMail', 'SEARCHBRUSER: 'BBSREAD.LASTERROR, msgs.i)
  467.         if result > 0 then do n = 1 to suser.COUNT
  468.             if suser.n.FOUNDINTAG = 1 then do
  469.                 drop duser. tuser.
  470.                 'READBRUSER BBSNAME "'globalcfg.SYSTEM'" USERNR 'suser.n.USERNR' DATASTEM 'duser' TAGSSTEM 'tuser
  471.                 if rc ~= 0 then call displayerror(10, 'SortMail', 'READBRUSER: 'BBSREAD.LASTERROR, msgs.i)
  472.                 if ~bittst(duser.FLAGS, UDB_DELETED) & (data.MSGDATE < duser.USERDATE + 2) & (data.MSGDATE > duser.USERDATE - 2) & (head.FROMNAME = tuser.NAME) then do
  473.                     'WRITEBRUSER BBSNAME "'globalcfg.SYSTEM'" UPDATEUSERNR 'suser.n.USERNR' DELETEUSER'
  474.                     if rc ~= 0 then call displayerror(30, 'SortMail', 'WRITEBRUSER: 'BBSREAD.LASTERROR, msgs.i)
  475.                     end
  476.                 end
  477.             end
  478.         end
  479.  
  480.     /* Delete original message? */
  481.     if dodelmsg = 1 then do
  482.         'UPDATEBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.conference'" 'msgs.i' SETDELETED'
  483.         if rc ~= 0 then displayerror(30, 'SortMail', 'UPDATEBRMESSAGE: 'BBSREAD.LASTERROR, msgs.i)
  484.         end
  485.     end
  486.  
  487. signal writelog
  488.  
  489. /*
  490. ** Some error detection stuff
  491. */
  492.  
  493. error:
  494. syntax:
  495.  
  496. select
  497.     when symbol('BBSREAD.LASTERROR') = 'VAR' then displayerror(rc, 'SortMail', 'Line 'sigl' in SortMail.br: 'BBSREAD.LASTERROR)
  498.     when symbol('THOR.LASTERROR') = 'VAR' then displayerror(rc, 'SortMail', 'Line 'sigl' in SortMail.br: 'THOR.LASTERROR)
  499.     otherwise displayerror(rc, 'SortMail', 'Error 'rc' in line 'sigl': 'errortext(rc))
  500.     end
  501.  
  502. /*
  503. ** Write log message
  504. */
  505.  
  506. writelog:
  507.  
  508. if fromthor then do
  509.     address(thorport)
  510.     'UPDATEPROGRESS REQ 'progwin' TOTAL 2 CURRENT 2 PT "Writing log message..."'
  511.     if rc = 5 then signal cleanup
  512.     if rc > 0 then call displayerror(rc, 'SortMail', 'UPDATEPROGRESS: 'THOR.LASTERROR)
  513.     end
  514. else say '1B'x'[1A' || '1B'x'[K' || 'Writing log...' || '1B'x'[0m'
  515.  
  516. if globalcfg.STATISTICS & totfound > 0 then do
  517.     logcount = logcount + 1; log.TEXT.logcount = 'SortMail processed 'msgs.count' messages and was triggered 'totfound' times, giving 'totfail' warnings/errors.'
  518.     logcount = logcount + 1; log.TEXT.logcount = ''
  519.     logcount = logcount + 1; log.TEXT.logcount = '*Trigger* *name*                          *Hits*      *Fails*'
  520.     do i = 1 to trigger.count
  521.         if trigger.i.hitcount > 0 then do
  522.             logcount = logcount + 1; log.TEXT.logcount = left(trigger.i.name, 39)' 'left(trigger.i.hitcount, 9)' 'trigger.i.failcount
  523.             end
  524.         end
  525.     logcount = logcount + 1; log.TEXT.logcount = ''
  526.  
  527.     do i = 1 to trigger.count
  528.         if symbol('trigger.i.log.count') = 'VAR' & trigger.i.log.count > 0 then do
  529.             do j = 1 to trigger.i.log.count
  530.                 logcount = logcount + 1; log.TEXT.logcount = trigger.i.log.j
  531.                 end
  532.             logcount = logcount + 1; log.TEXT.logcount = ''
  533.             end
  534.         end
  535.     end
  536.  
  537. if aminetlogcount > 0 then do
  538.     do i = 1 to aminetlogcount
  539.         logcount = logcount + 1;  log.TEXT.logcount = aminetlog.TEXT.i
  540.         end
  541.     logcount = logcount + 1;  log.TEXT.logcount = ''
  542.     end
  543.  
  544. if errlogcount > 0 then do
  545.     logcount = logcount + 1; log.TEXT.logcount = '*Warnings* *and* *errors*'
  546.     logcount = logcount + 1; log.TEXT.logcount = ''
  547.     do i = 1 to errlogcount
  548.         logcount = logcount + 1; log.TEXT.logcount = errlog.TEXT.i
  549.         end
  550.     end
  551.  
  552. if logcount ~= 0 then do
  553.     log.fromname   = 'SortMail'
  554.     log.toname     = bbsdata.USERNAME
  555.     log.toaddr     = bbsdata.EMAILADDR
  556.     log.subject    = 'SortMail results'
  557.     log.text.count = logcount
  558.  
  559.     call writemessage('"'globalcfg.SYSTEM'"' '"'globalcfg.CONFERENCE'"' log)
  560.     if result ~= 0 then do
  561.         if bbsdata.DNLOADPATH = '' then do
  562.             address(bbsread)
  563.             'GETGLOBALDATA 'globaldata
  564.             if rc = 0 then logpath = globaldata.DNLOADPATH
  565.             end
  566.         else logpath = bbsdata.DNLOADPATH
  567.  
  568.         if symbol('logpath') = 'VAR' then do
  569.             if right(logpath, 1) ~= '/' & right(logpath, 1) ~= ':' then logpath = logpath'/'
  570.             call notify('SortMail couldn''t write the log as a message.\nDo you want to save it to 'logpath'SortMail.log?', 'Yes|No')
  571.             if result = 1 then do
  572.                 lfopen = open(lf, logpath'SortMail.log', 'W')
  573.                 if lfopen then do
  574.                     dtg = date('E')' at 'time('N'); call writeln(lf, 'SortMail results on 'dtg'.'); call writeln(lf, '')
  575.                     do i = 1 to log.text.count; call writeln(lf, log.text.i); end
  576.                     call close(lf)
  577.                     end
  578.                 else displayerror(30, 'SortMail', 'Couldn''t open 'logpath'SortMail.log for writing.')
  579.                 end
  580.             end
  581.         else displayerror(30, 'SortMail',' Couldn''t write log to file.')
  582.         end
  583.     drop log.
  584.     end
  585.  
  586. break_c:
  587. halt:
  588. cleanup:
  589.  
  590. if exists('T:SortMail.result') then 'Delete T:SortMail.result QUIET'
  591.  
  592. /*
  593. ** Turn off copyback buffer
  594. */
  595.  
  596. address(bbsread)
  597. 'BUFMODE ENDCOPYBACK'
  598.  
  599. /*
  600. ** Update message counter
  601. */
  602.  
  603. if symbol('counter') = 'VAR' & symbol('args.MSGNO.COUNT') ~= 'VAR' & ~args.SHANGHAI then do
  604.     cnopen = open(cn, bbsdata.BBSPATH || 'SortMail.count', 'W')
  605.     if cnopen then do
  606.         call writeln(cn, counter)
  607.         call close(cn)
  608.         end
  609.     end
  610.  
  611. /*
  612. ** Close progressbar if open
  613. */
  614.  
  615. if (symbol('progwin') = 'VAR') & (progwin ~= 0)  then do
  616.     address(thorport)
  617.     'CLOSEPROGRESS REQ 'progwin
  618.     progwin = 0
  619.  
  620.     if (symbol('totfound') = 'VAR') & (totfound > 0) then do
  621.         'CURRENTSYSTEM STEM 'cursys
  622.         if rc > 1 then call displayerror(30, 'SortMail', 'CURRENTSYSTEM: 'THOR.LASTERROR)
  623.         if (upper(cursys.CONFNAME) = upper(globalcfg.CONFERENCE)) then 'SHOWCONFERENCE "'globalcfg.CONFERENCE'"'
  624.         'UPDATECONFWINDOW'
  625.         end
  626.     end
  627.  
  628. /*
  629. ** Remove ARexx clips
  630. */
  631.  
  632. call setclip('SM_System')
  633. call setclip('SM_Conference')
  634. call setclip('SM_ThorPort')
  635. call setclip('SM_MsgNo')
  636. call setclip('SM_Active')
  637.  
  638. /*
  639. ** Have a nice day
  640. */
  641.  
  642. exit(0)
  643.  
  644.  
  645.  /****************************************************************************
  646. **************************** Run an external script ***************************
  647.  ****************************************************************************/
  648.  
  649. runexternal: interpret 'procedure expose 'globals
  650.              parse arg scriptname, scriptopts, msgno
  651.  
  652. scriptopts = substitute(scriptopts, "%s", '"'globalcfg.SYSTEM'"')
  653. scriptopts = substitute(scriptopts, "%c", '"'globalcfg.conference'"')
  654. scriptopts = substitute(scriptopts, "%n", msgno)
  655. if fromthor then scriptopts = substitute(scriptopts, "%p", '"'thorport'"')
  656. else scriptopts = substitute(scriptopts, "%p", '"NONE"')
  657.  
  658. if index(scriptname, ':') > 0 then scriptpath = scriptname
  659. else scriptpath = thorpath || scriptname
  660.  
  661. address(command)
  662. 'rx >T:SortMail.result 'scriptpath' 'scriptopts
  663. returned = rc
  664.  
  665. if returned > 0 then do
  666.     resopen = open(rf, 'T:SortMail.result', 'R')
  667.     if resopen then do
  668.         res = readln(rf)
  669.         if left(res, 20) = 'rx failed returncode' then do
  670.             res2 = readln(rf)
  671.             if res2 ~= '' then res = res2
  672.             end
  673.         call close(rf)
  674.         end
  675.     else res = 'Unknown error'
  676.  
  677.     call displayerror(returned, 'External script 'scriptpath, res, msgno)
  678.     end
  679.  
  680. if exists('T:SortMail.result') then 'Delete T:SortMail.result QUIET'
  681.  
  682. return(returned)
  683.  
  684.  
  685.  /****************************************************************************
  686. ************************* Parse AmiNet RECENT updates *************************
  687.  ****************************************************************************/
  688.  
  689. parseaminet: interpret 'procedure expose 'globals
  690.              parse arg system, conference, number, checkdupes, dontadd, nostats
  691.  
  692. curline = 1; res = 0
  693.  
  694. /*
  695. ** Compensate for a bug in bbsread on OS2.x systems
  696. */
  697.  
  698. if right(bbsdata.BBSPATH, 1) ~= ':' & right(bbsdata.BBSPATH, 1) ~= '/' then bbsdata.BBSPATH = bbsdata.BBSPATH'/'
  699.  
  700. /*
  701. ** Read message text if it isn't already read
  702. */
  703.  
  704. address(bbsread)
  705. if textread = 0 then do
  706.     'READBRMESSAGE "'system'" "'conference'" 'number' TEXTSTEM 'text
  707.     if rc ~= 0 then do
  708.         call displayerror(10, 'AmiNet RECENT parser', 'READBRMESSAGE, text stem: 'BBSREAD.LASTERROR, number)
  709.         return(10)
  710.         end
  711.     textread = 1
  712.     end
  713.  
  714. /*
  715. ** Exit if it doesn't start with "|" (then it's probably not a RECENT msg)
  716. */
  717.  
  718. if left(text.TEXT.curline, 1) ~= '|' then do
  719.     call displayerror(5, 'AmiNet RECENT parser', 'Not a AmiNet RECENT message.', number)
  720.     return(5)
  721.     end
  722.  
  723. /*
  724. ** Read exclude file
  725. */
  726.  
  727. openexcl = open(ef, bbsdata.BBSPATH || 'SortMail.excl', 'R')
  728. if openexcl then do
  729.     cnt = 0
  730.     do until eof(ef)
  731.         entry = readln(ef)
  732.         if entry ~= '' then do; cnt = cnt + 1; excldir.cnt = entry; end
  733.         end
  734.     excldir.count = cnt
  735.     call close(ef)
  736.     end
  737.  
  738. /*
  739. ** Skip all lines beginning with "|"
  740. */
  741.  
  742. do while(left(text.TEXT.curline, 1) = '|'); curline = curline + 1; end
  743.  
  744. /*
  745. ** Exit if there are no new files
  746. */
  747.  
  748. if curline >= text.TEXT.COUNT then do
  749.     call displayerror(5, 'AmiNet RECENT parser', 'No new files in message.', number)
  750.     return(5)
  751.     end
  752.  
  753. /*
  754. ** Update NewFiles.txt, delete it first if there's a old one there already
  755. */
  756.  
  757. if (delnewfiles = 0) & (exists(bbsdata.BBSPATH'Newfiles.txt')) then do
  758.     address(command)
  759.     'Delete "'bbsdata.BBSPATH'Newfiles.txt" QUIET'
  760.     delnewfiles = 1
  761.     end
  762.  
  763. if exists(bbsdata.BBSPATH'Newfiles.txt') then call open(ar, bbsdata.BBSPATH'Newfiles.txt', 'A')
  764. else call open(ar, bbsdata.BBSPATH'Newfiles.txt', 'W')
  765.  
  766. /*
  767. ** Process the RECENT message
  768. */
  769.  
  770. address(bbsread)
  771.  
  772. do until curline = text.TEXT.COUNT
  773.     aline = text.TEXT.curline
  774.     if left(aline, 1) = '|' then signal writestats
  775.     if aline ~= "" then do
  776.         delfile = 0; drop found. filetags.
  777.  
  778.         if (dontadd = 1) then addfile = 0; else addfile = 1
  779.  
  780.         if (aline) = 'Message of the day:' then signal writemotd
  781.  
  782.         farea = word(aline, 2)
  783.  
  784.         if (addfile) & (openexcl) then do i = 1 to excldir.count
  785.             if (index(excldir.i, '/') > 0) & (farea = excldir.i) then addfile = 0
  786.             else if left(farea, length(excldir.i)) = excldir.i then addfile = 0
  787.             end
  788.  
  789.         if (addfile) then do
  790.             fname = word(aline, 1)
  791.             fdesc = right(aline, length(aline) - 35)
  792.  
  793.             fsize = right(left(aline, 34), 5)
  794.             if right(fsize, 1) = 'M' then mega = 1
  795.             else mega = 0
  796.             fsize = compress(fsize, 'KM .')
  797.  
  798.             if ~datatype(fsize, 'W') then fsize = 0
  799.             fsize = fsize * 1024
  800.             if mega = 1 then fsize = trunc((fsize * 1024) / 10)
  801.  
  802.             if checkdupes then do
  803.                 'SEARCHBRFILE BBSNAME "'system'" FAREANAME "'farea'" SEARCH "'fname'" NAME STEM 'found
  804.                 if rc = 6 then drop BBSREAD.LASTERROR
  805.                 else if rc ~= 0 then do
  806.                     res = rc
  807.                     call displayerror(res, 'AmiNet RECENT parser', 'SEARCHBRFILE: 'BBSREAD.LASTERROR, number)
  808.                     end
  809.                 else if result > 0 then do i = 1 to found.FILE.1.COUNT
  810.                     'READBRFILE BBSNAME "'system'" FAREANAME "'farea'" FILENR 'found.FILE.1.i' TAGSSTEM 'filetags
  811.                     if rc ~= 0 then  do
  812.                         res = rc
  813.                         call displayerror(res, 'AmiNet RECENT parser', 'READBRFILE: 'BBSREAD.LASTERROR, number)
  814.                         end
  815.                     else if filetags.DESCRIPTION.1 ~= fdesc & (fsize > filetags.SIZE + 1023 | fsize < filetags.SIZE - 1024) then do
  816.                         'WRITEBRFILE BBSNAME "'system'" FAREANAME "'farea'" UPDATEFILENR 'found.FILE.1.1' DELETEFILE'
  817.                         if rc ~= 0 then do
  818.                             res = rc
  819.                             call displayerror(res, 'AmiNet RECENT parser', 'WRITEBRFILE: 'BBSREAD.LASTERROR, number)
  820.                             end
  821.                         delfile = 1
  822.                         end
  823.                     if delfile = 0 then addfile = 0
  824.                     end
  825.                 end
  826.             end
  827.  
  828.         if addfile & fdesc ~= '' then do
  829.             'CONFIGFAREA "'system'" "'farea'"'
  830.             if rc ~= 0 then call displayerror(rc, 'AmiNet RECENT parser', 'CONFIGFAREA: 'BBSREAD.LASTERROR, number)
  831.  
  832.             drop brfile.
  833.             brfile.NAME = fname
  834.             brfile.SIZE = fsize
  835.             brfile.DATE = head.CREATIONDATE
  836.             brfile.DESCRIPTION.COUNT = 1
  837.             brfile.DESCRIPTION.1 = strip(fdesc)
  838.  
  839.             'WRITEBRFILE "'system'" "'farea'" STEM 'brfile
  840.             if rc ~= 0 then do
  841.                 call displayerror(rc, 'AmiNet RECENT parser', 'WRITEBRFILE: 'BBSREAD.LASTERROR, number)
  842.                 res = rc
  843.                 end
  844.             end
  845.  
  846.         call writeln(ar, aline)
  847.         end
  848.     curline = curline + 1
  849.     end
  850.  
  851. signal amifini
  852.  
  853. /*
  854. ** Write statistics from AmiNet
  855. */
  856.  
  857. writestats:
  858.  
  859. if nostats = 1 then signal amifini
  860.  
  861. aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = '*AmiNet* *Statistics*'
  862. aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = ''
  863.  
  864. do i = curline to text.TEXT.COUNT
  865.     aline = text.TEXT.i
  866.     if aline = 'Message of the day:' then do
  867.         curline = i
  868.         signal writemotd
  869.         end
  870.     aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = aline
  871.     end
  872. aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = ''
  873.  
  874. signal amifini
  875.  
  876. /*
  877. ** Write AmiNet Message of the Day
  878. */
  879.  
  880. writemotd:
  881.  
  882. if nostats = 1 then signal amifini
  883.  
  884. address(bbsread)
  885. 'AMIGA2DATE SECONDS 'head.CREATIONDATE' STEM 'time
  886. if rc ~= 0 then displayerror(5, 'AmiNet Message of the Day', BBSREAD.LASTERROR, number)
  887.  
  888. aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = '*AmiNet* *Message* *of* *the* *Day*: 'time.MDAY'.'time.MONTH'.'time.YEAR
  889. aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = ''
  890.  
  891. do i = curline to text.TEXT.COUNT
  892.     aline = text.TEXT.i
  893.     if left(aline, 1) = '|' then do
  894.         curline = i
  895.         signal writestats
  896.         end
  897.     aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = aline
  898.     end
  899. aminetlogcount = aminetlogcount + 1; aminetlog.text.aminetlogcount = ''
  900.  
  901. /*
  902. ** Clean up and return
  903. */
  904.  
  905. amifini:
  906. call close(ar)
  907.  
  908. return(res)
  909.  
  910.  
  911.  /****************************************************************************
  912. ******************************** Copy messages ********************************
  913.  *****************************************************************************/
  914.  
  915. copymsg: interpret 'procedure expose 'globals
  916.          parse arg system, mailconf, number, toconf, repaddr, tosys
  917.  
  918. /*
  919. ** Read text stem if it's not already read
  920. */
  921.  
  922. address(bbsread)
  923. if textread = 0 then do
  924.     'READBRMESSAGE "'system'" "'mailconf'" 'number' TEXTSTEM 'text
  925.     if rc ~= 0 then do
  926.         call displayerror(10, 'Message copy', BBSREAD.LASTERROR, number)
  927.         return(10)
  928.         end
  929.     textread = 1
  930.     end
  931.  
  932. if text.TEXT.COUNT = 0 & (text.PART.COUNT = 0 | symbol('text.PART.COUNT') ~= 'VAR') then do
  933.     text.TEXT.1 = ' '; text.TEXT.COUNT = 1
  934.     end
  935.  
  936. if head.fromname        ~= "HEAD.FROMNAME"        then text.fromname        = head.fromname
  937. if head.fromaddr        ~= "HEAD.FROMADDR"        then text.fromaddr        = head.fromaddr
  938. if head.toname          ~= "HEAD.TONAME"          then text.toname          = head.toname
  939. if head.toaddr          ~= "HEAD.TOADDR"          then text.toaddr          = head.toaddr
  940. if head.msgid           ~= "HEAD.MSGID"           then text.msgid           = head.msgid
  941. if head.refid           ~= "HEAD.REFID"           then text.refid           = head.refid
  942. if head.creationdate    ~= "HEAD.CREATIONDATE"    then text.creationdate    = head.creationdate
  943. if head.creationdatetxt ~= "HEAD.CREATIONDATETXT" then text.creationdatetxt = head.creationdatetxt
  944. if head.subject         ~= "HEAD.SUBJECT"         then text.subject         = head.subject
  945.  
  946. text.replyconf = mailconf
  947.  
  948. drop text.replyname
  949. if (repaddr = '') | (right(repaddr, 9) = 'REPLYADDR') then do
  950.     if symbol('text.replyaddr') ~= 'VAR' then do
  951.         text.replyaddr = head.fromaddr
  952.         if symbol('head.fromname') = 'VAR' then text.replyname = head.fromname
  953.         end
  954.     end
  955. else text.replyaddr = repaddr
  956.  
  957. repl = ""; priv = ""; kep = ""; urg = ""; imp = ""; conf = ""
  958. if bittst(data.flags,  1) then repl = "REPLIED"
  959. if bittst(data.flags,  2) then priv = "PRIVATE"
  960. if bittst(data.flags,  7) then kep  = "KEEP"
  961. if bittst(data.flags, 11) then urg  = "URGENT"
  962. if bittst(data.flags, 12) then imp  = "IMPORTANT"
  963. if bittst(data.flags, 17) then conf = "CONFIDENTIAL"
  964.  
  965. if (tosys = '') | (right(tosys, 7) = 'DESTSYS') then call writemessage('"'system'"' '"'toconf'"' text repl priv kep urg imp conf HAZELEVEL data.HAZELEVEL)
  966. else call writemessage('"'tosys'"' '"'toconf'"' text repl priv kep urg imp conf HAZELEVEL data.HAZELEVEL)
  967.  
  968. return(result)
  969.  
  970.  
  971.  /****************************************************************************
  972. ***************************** Save message to disk ****************************
  973.  ****************************************************************************/
  974.  
  975. savemessage: interpret 'procedure expose 'globals
  976.              parse arg msgno, desttype, destname, header, append, nobin, subst, with
  977.  
  978. /*
  979. ** Find download path
  980. */
  981.  
  982. address(bbsread)
  983. if symbol('bbsdata.DNLOADPATH') ~= 'VAR' | bbsdata.DNLOADPATH = '' then do
  984.     'GETGLOBALDATA 'globaldata
  985.     if rc ~= 0 then do
  986.         call displayerror(returned, 'Save message', 'GETGLOBALDATA: 'BBSREAD.LASTERROR, msgno)
  987.         return(returned)
  988.         end
  989.     downloadpath = globaldata.DNLOADPATH
  990.     end
  991. else downloadpath = bbsdata.DNLOADPATH
  992.  
  993. if right(downloadpath, 1) ~= ':' & right(downloadpath, 1) ~= '/' then downloadpath = downloadpath'/'
  994.  
  995. /*
  996. ** Find path, filename and mode of output file
  997. */
  998.  
  999. select
  1000.     when desttype = 0 then do
  1001.         destfile = destname
  1002.         if (append) & (exists(fileame)) then filemode = 'A'
  1003.         else filemode = 'W'
  1004.         end
  1005.  
  1006.     when desttype = 1 then do
  1007.         destfile = head.SUBJECT
  1008.  
  1009.         if subst ~= '' then do
  1010.             if with ~= '' then destfile = substitute(destfile, subst, with)
  1011.             else do
  1012.                 call displayerror(20, 'Save message', 'SUBSTITUTE/K needs WITH/K.', msgno)
  1013.                 return(20)
  1014.                 end
  1015.             end
  1016.  
  1017.         /* Strip unwanted characters and "Re: " from subject */
  1018.         destfile = compress(destfile, '*')
  1019.         destfile = compress(destfile, '#')
  1020.         destfile = compress(destfile, '?')
  1021.         destfile = compress(destfile, '`')
  1022.         destfile = compress(destfile, '/')
  1023.         destfile = compress(destfile, ':')
  1024.         do while upper(left(destfile, 3)) = 'RE '
  1025.             if upper(left(destfile, 3)) = 'RE ' then destfile = substr(destfile, 4)
  1026.             end
  1027.  
  1028.         if right(destname, 1) ~= ':' & right(destname, 1) ~= '/' then destname = destname'/'
  1029.  
  1030.         destfile = destname || destfile
  1031.  
  1032.         if (append) & (exists(destfile)) then filemode = 'A'
  1033.         else filemode = 'W'
  1034.         end
  1035.  
  1036.     otherwise do
  1037.         call displayerror(20, 'Save Message', 'Neither DIRECTORY/K nor FILENAME/K were specified.', msgno)
  1038.         return(20)
  1039.         end
  1040.     end
  1041.  
  1042. fileopen = 0
  1043.  
  1044. if symbol('thorport') = 'VAR' & thorport ~= 'NONE' then do
  1045.     if ~nobin then do
  1046.         address(bbsread)
  1047.         if ~textread then do
  1048.             'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.CONFERENCE'" 'msgno' TEXTSTEM 'text
  1049.             returned = rc
  1050.             if rc > 0 then do
  1051.                 call displayerror(returned, 'Save message', 'READBRMESSAGE: 'BBSREAD.LASTERROR, msgno)
  1052.                 return(returned)
  1053.                 end
  1054.             textread = 1
  1055.             end
  1056.         call checkbin('text', msgno, downloadpath)
  1057.         returned = result
  1058.         end
  1059.  
  1060.     saveargs = ''
  1061.     if ~header then saveargs = saveargs' NOHEADER'
  1062.     if ~append then saveargs = saveargs' OVERWRITE'
  1063.  
  1064.     address(thorport)
  1065.     'SAVEMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.CONFERENCE'" MSGNR 'msgno' FILE "'destfile'" 'saveargs
  1066.     smreturned = rc
  1067.     if rc ~= 0 then do
  1068.         call displayerror(smreturned, 'Save Message', 'SAVEMESSAGE: 'THOR.LASTERROR, msgno)
  1069.         end
  1070.     if symbol('returned') ~= 'VAR' then returned = 0
  1071.     if smreturned > returned then returned = smreturned; drop smreturned
  1072.     end
  1073. else do
  1074.     if textread = 0 then do
  1075.         address(bbsread)
  1076.         'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.CONFERENCE'" 'msgno' TEXTSTEM 'text
  1077.         returned = rc
  1078.         if rc > 0 then do
  1079.             call displayerror(returned, 'Save message', 'READBRMESSAGE: 'BBSREAD.LASTERROR, msgno)
  1080.             return(returned)
  1081.             end
  1082.         textread = 1
  1083.         end
  1084.  
  1085.     call savemsg('head', 'text', destfile, filemode, header, nobin, msgno, downloadpath)
  1086.     returned = rc
  1087.     end
  1088.  
  1089. if fileopen then do
  1090.     call close(of)
  1091.     fileopen = 0
  1092.     end
  1093.  
  1094. return(returned)
  1095.  
  1096.  
  1097.  /****************************************************************************
  1098. ******************* Check if a message contains binary parts ******************
  1099.  ****************************************************************************/
  1100.  
  1101. checkbin: interpret 'procedure expose 'globals
  1102.           parse arg tstem, msgno, downloadpath
  1103.  
  1104. /*
  1105. ** Check for message parts
  1106. */
  1107.  
  1108. if symbol(tstem'.PART.COUNT') = 'VAR' then do
  1109.     parts = value(tstem'.PART.COUNT')
  1110.     if parts > 0 then do i = 1 to parts
  1111.         select
  1112.          when symbol(tstem'.PART.'i'.BINARY') = 'VAR' then do
  1113.                 if exists(value(tstem'.PART.'i'.BINARY')) then do
  1114.                     address(command)
  1115.                     'Copy "'value(tstem'.PART.'i'.BINARY')'" TO "'downloadpath'" CLONE QUIET'
  1116.                     if rc ~= 0 then do
  1117.                         call displayerror(20, 'Save message', 'Failed to copy binary part to download directory.', msgno)
  1118.                         return(20)
  1119.                         end
  1120.                     address(bbsread)
  1121.                     end
  1122.                 end
  1123.  
  1124.             otherwise do
  1125.                 usestem = tstem'.PART.'i'.MSG'
  1126.                 call checkbin(usestem, msgno, downloadpath)
  1127.                 end
  1128.             end
  1129.         end
  1130.     end
  1131.  
  1132. return(0)
  1133.  
  1134.  
  1135.  /****************************************************************************
  1136. *************** Recursive procedure for writing message to file ***************
  1137.  ****************************************************************************/
  1138.  
  1139. savemsg: interpret 'procedure expose 'globals
  1140.          parse arg hstem, tstem, filename, filemode, header, nobin, msgno, downloadpath
  1141.  
  1142. /*
  1143. ** Open file for writing/appending
  1144. */
  1145.  
  1146. if ~fileopen then do
  1147.     fileopen = open(of, filename, filemode)
  1148.     if fileopen & filemode = 'A' then call writeln(of, copies('=', 79))
  1149.     end
  1150.  
  1151. if ~fileopen then do
  1152.     call displayerror(20, 'Save Message', 'Couldn''t open "'filename'" for writing.', msgno)
  1153.     return(20)
  1154.     end
  1155.  
  1156. /*
  1157. ** Write to/from names/addresses, subject and header
  1158. */
  1159.  
  1160. if header then do
  1161.     if symbol(hstem.'FROMNAME') = 'VAR' then do
  1162.         from = value(hstem'.FROMNAME')
  1163.         if symbol(hstem'.FROMADDR') = "VAR" then from = from || ' <' || value(hstem'.FROMADDR') || '>'
  1164.         end
  1165.     else do
  1166.         from = head.FROMNAME
  1167.         if symbol('head.FROMADDR') = "VAR" then from = from || ' <' || head.FROMADDR || '>'
  1168.         end
  1169.     call writeln(of, 'From: 'from)
  1170.  
  1171.     if symbol(hstem'.TONAME') = "VAR" then do
  1172.         to = value(hstem'.TONAME')
  1173.         if symbol(hstem'.TOADDR') = "VAR" then to = to || ' <' || value(hstem'.TOADDR') || '>'
  1174.         call writeln(of, 'To: 'to)
  1175.         end
  1176.  
  1177.     if symbol(hstem'.SUBJECT') = 'VAR' then call writeln(of, 'Subject: 'value(hstem'.SUBJECT'))
  1178.     else call writeln(of, 'Subject: 'head.SUBJECT)
  1179.  
  1180.     if symbol(tstem'.COMMENT.COUNT') = "VAR" then do
  1181.         cnt = value(tstem'.COMMENT.COUNT')
  1182.         if cnt > 0 then do
  1183.             do i = 1 to cnt; call writeln(of, value(tstem'.COMMENT.'i)); end
  1184.             end
  1185.         end
  1186.     end
  1187.  
  1188. /*
  1189. ** Write body text
  1190. */
  1191.  
  1192. if symbol(tstem'.TEXT.COUNT') = "VAR" then do
  1193.     cnt = value(tstem'.TEXT.COUNT')
  1194.     if cnt > 0 then do
  1195.         call writeln(of, '')
  1196.         do i = 1 to cnt; call writeln(of, value(tstem'.TEXT.'i)); end
  1197.         end
  1198.     end
  1199.  
  1200. /*
  1201. ** Check for message parts
  1202. */
  1203.  
  1204. if symbol(tstem'.PART.COUNT') = "VAR" then do
  1205.  
  1206.     parts = value(tstem'.PART.COUNT')
  1207.     if parts > 0 then do i = 1 to parts
  1208.         select
  1209.             when symbol(tstem'.PART.'i'.BINARY') = "VAR" then do
  1210.                 call writeln(of, '')
  1211.  
  1212.                 cnt = 0
  1213.                 if symbol(tstem'.PART.'i'.BINARY.COMMENT.COUNT') = VAR & header then cnt = value(tstem'.PART.'i'.BINARY.COMMENT.COUNT')
  1214.                 if cnt > 0 then do
  1215.                     call writeln(of, '')
  1216.                     do j = 1 to cnt
  1217.                         call writeln(of, value(tstem'.PART.'i'.BINARY.COMMENT.'j))
  1218.                         end
  1219.                     end
  1220.  
  1221.                 call writeln(of, '[Binary part: 'value(tstem'.PART.'i'.BINARY')']')
  1222.  
  1223.                 if ~nobin then do
  1224.                     if exists(value(tstem'.PART.'i'.BINARY')) then do
  1225.                         address(command)
  1226.                         'Copy "'value(tstem'.PART.'i'.BINARY')'" TO "'downloadpath'" CLONE QUIET'
  1227.                         if rc ~= 0 then do
  1228.                             call displayerror(20, 'Save message', 'Failed to copy binary part to download directory.', msgno)
  1229.                             return(20)
  1230.                             end
  1231.                         address(bbsread)
  1232.                         call writeln(of, '[Binary part copied to: 'downloadpath']')
  1233.                         end
  1234.                     else call writeln(of, '[Binary part "'value(tstem'.PART.'i'.BINARY')'" was already deleted]')
  1235.                     end
  1236.                 end
  1237.  
  1238.             when symbol(tstem'.PART.'i'.COMMENT.COUNT') = "VAR" & header then do
  1239.                 cnt = value(tstem'.PART.'i'.COMMENT.COUNT')
  1240.                 if cnt > 0 then do
  1241.                     do j = 1 to cnt
  1242.                         call writeln(of, value(tstem'.PART.'i'.COMMENT.'j))
  1243.                         end
  1244.                     end
  1245.                 end
  1246.  
  1247.             when symbol(tstem'.PART.'i'.TEXT.COUNT') = "VAR" then do
  1248.                 cnt = value(tstem'.PART.'i'.TEXT.COUNT')
  1249.                 if cnt > 0 then do
  1250.                     call writeln(of, '')
  1251.                     do j = 1 to cnt
  1252.                         call writeln(of, value(tstem'.PART.'i'.TEXT.'j))
  1253.                         end
  1254.                     end
  1255.                 end
  1256.  
  1257.             otherwise do
  1258.                 call writeln(of, copies('=', 79))
  1259.                 usestem = tstem'.PART.'i'.MSG'
  1260.                 call savemsg(usestem, usestem, filename, filemode, header, nobin, msgno, downloadpath)
  1261.                 end
  1262.             end
  1263.         end
  1264.     end
  1265.  
  1266. return(0)
  1267.  
  1268.  
  1269.  /****************************************************************************
  1270. ************************** Write message to database **************************
  1271.  ****************************************************************************/
  1272.  
  1273. writemessage: interpret 'procedure expose 'globals
  1274.               parse arg wmarguments
  1275.  
  1276. /*
  1277. ** Initialize arguments and parse them
  1278. */
  1279.  
  1280. wmtemplate = 'SYSTEM/A,CONFERENCE/A,MSGSTEM/A,DONTMARKMESSAGE/S,REPLIED/S,PRIVATE/S,KEEP/S,READ/S,URGENT/S,IMPORTANT/S,CONFIDENTIAL/S,HAZELEVEL/K/N'
  1281.  
  1282. CDB_MARK_OWN_MSGS      = 22           /* Also mark messages from user when adding messages. */
  1283. CDF_NOT_ON_BBS         = '00008000'x  /* This conference is not on the bbs. */
  1284.  
  1285. wmargs.DONTMARKMESSAGE = 0; wmargs.PRIVATE      = 0; wmargs.READ = 0;    wmargs.URGENT  = 0
  1286. wmargs.IMPORTANT       = 0; wmargs.CONFIDENTIAL = 0; wmargs.KEEP = 0;    wmargs.REPLIED = 0
  1287. wmargs.HAZELEVEL       = 0
  1288.  
  1289. address(bbsread)
  1290. 'READARGS 'wmtemplate wmargs' CMDLINE 'wmarguments
  1291. if rc ~= 0 then do
  1292.     call displayerror(10, 'Write message', 'READARGS: 'BBSREAD.LASTERROR)
  1293.     return(10)
  1294.     end
  1295.  
  1296. /*
  1297. ** See if the conference the msg will be written to exists
  1298. */
  1299.  
  1300. if conflist.system ~= wmargs.SYSTEM then do
  1301.     'GETCONFLIST BBSNAME "'wmargs.SYSTEM'" STEM 'conflist
  1302.     if rc ~= 0 then call displayerror(30, 'SortMail', 'GETCONFLIST: 'BBSREAD.LASTERROR)
  1303.     conflist.system = wmargs.SYSTEM
  1304.     end
  1305.  
  1306. do n = 1 to conflist.COUNT + 1 while upper(wmargs.CONFERENCE) ~= upper(conflist.n)
  1307.     if n = conflist.COUNT + 1 then do
  1308.         /* Create the new conference */
  1309.         'CONFIGCONF "'wmargs.SYSTEM'" "'wmargs.CONFERENCE'" SET 'c2x(CDF_NOT_ON_BBS)
  1310.         if rc ~= 0 then do
  1311.             call displayerror(30, 'Write message', 'CONFIGCONF: 'BBSREAD.LASTERROR)
  1312.             return(30)
  1313.             end
  1314.         conflist.n = toconf
  1315.         conflist.COUNT = conflist.COUNT + 1
  1316.         end
  1317.     end
  1318.  
  1319. /*
  1320. ** If Show own messages isn't activated in conference mark then don't
  1321. ** mark message as unread.
  1322. */
  1323.  
  1324. 'GETCONFDATA "'wmargs.SYSTEM'" "'wmargs.CONFERENCE'" STEM 'confdata
  1325. if rc ~= 0 then do
  1326.     returned = rc
  1327.     call displayerror(returned, 'Write message', 'GETCONFDATA: 'BBSREAD.LASTERROR)
  1328.     return(returned)
  1329.     end
  1330.  
  1331. if ~bittst(confdata.FLAGS, CDB_MARK_OWN_MSGS) & (value(wmargs.msgstem'.fromaddr') = bbsdata.EMAILADDR) then wmargs.DONTMARKMESSAGE = 1
  1332.  
  1333. /*
  1334. ** Set the selected message flags
  1335. */
  1336.  
  1337. writeflags = ''
  1338. if wmargs.DONTMARKMESSAGE then writeflags = writeflags'DONTMARKMESSAGE '
  1339. if wmargs.PRIVATE         then writeflags = writeflags'PRIVATE '
  1340. if wmargs.READ            then writeflags = writeflags'READ '
  1341. if wmargs.URGENT          then writeflags = writeflags'URGENT '
  1342. if wmargs.IMPORTANT       then writeflags = writeflags'IMPORTANT '
  1343. if wmargs.CONFIDENTIAL    then writeflags = writeflags'CONFIDENTIAL '
  1344.  
  1345. updateflags = ''
  1346. if wmargs.KEEP            then updateflags = updateflags'SETKEEP '
  1347. if wmargs.REPLIED         then updateflags = updateflags'SETREPLIED '
  1348. if wmargs.HAZELEVEL > 0   then updateflags = updateflags'HAZELEVEL 'wmargs.HAZELEVEL' '
  1349.  
  1350. /*
  1351. ** Write the message
  1352. */
  1353.  
  1354. address(bbsread)
  1355. 'WRITEBRMESSAGE "'wmargs.SYSTEM'" "'wmargs.CONFERENCE'" STEM 'wmargs.MSGSTEM
  1356. msgnr = result
  1357.  
  1358. if rc ~= 0 then do
  1359.     returned = rc
  1360.     call displayerror(returned, 'Write message', 'WRITEBRMESSAGE: 'BBSREAD.LASTERROR)
  1361.     return(returned)
  1362.     end
  1363.  
  1364. /*
  1365. ** Give the new message it's flags
  1366. */
  1367.  
  1368. if updateflags ~= '' then do
  1369.     'UPDATEBRMESSAGE "'wmargs.SYSTEM'" "'wmargs.CONFERENCE'" 'msgnr updateflags
  1370.     if rc ~= 0 then do
  1371.         returned = rc
  1372.         call displayerror(returned, 'Write message', 'UPDATEBRMESSAGE: 'BBSREAD.LASTERROR, msgnr)
  1373.         return(returned)
  1374.         end
  1375.     end
  1376.  
  1377. return(0)
  1378.  
  1379.  
  1380.  /****************************************************************************
  1381. *********************** Open and read configuration file **********************
  1382.  ****************************************************************************/
  1383.  
  1384. readcfg: interpret 'procedure expose 'globals
  1385.  
  1386. triggers = 0
  1387.  
  1388. address(bbsread)
  1389.  
  1390. cfgopen = open(cf, bbsdata.BBSPATH || cfgfile, 'R')
  1391.  
  1392. if cfgopen then do
  1393.     cfglength = seek(cf, 0, 'E'); call seek(cf, 0, 'B')
  1394.  
  1395.     cfgline = 0
  1396.  
  1397.     do until (seek(cf, 0) = cfglength)
  1398.         entry = readln(cf); cfgline = cfgline + 1
  1399.  
  1400.         if symbol('progwin') = 'VAR' then do
  1401.             address(thorport)
  1402.             'UPDATEPROGRESS 'progwin' CURRENT 'seek(cf, 0)' TOTAL 'cfglength
  1403.             if rc = 5 then signal cleanup
  1404.             if rc ~= 0 then displayerror(rc, 'Read config', 'UPDATEPROGRESS: 'THOR.LASTERROR)
  1405.             address(bbsread)
  1406.             end
  1407.  
  1408.         select
  1409.             when upper(subword(entry, 1, 1)) = "SYSTEM" then displayerror(30, 'Read config', 'Found old 2.x configuration file.  SortMail has\nchanged the configuration file format in version\n3.0.  Please create a new one with CfgSortMail.thor.')
  1410.  
  1411.             when upper(subword(entry, 1, 1)) = "GLOBAL" then do
  1412.                 globalcfg.STATISTICS = 0; globalcfg.NOWARN = 0; globalcfg.LOGINSTATE = 0
  1413.                 'READARGS TEMPLATE "SYSTEM/K,CONFERENCE/A,STATISTICS/S,NOWARN/S,LOGINSTATE/S" STEM 'globalcfg' CMDLINE 'subword(entry, 2)
  1414.                 if rc ~= 0 then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  1415.  
  1416.                 /*
  1417.                 ** Exit of user's in the wrong system or there's no marked msgs
  1418.                 */
  1419.  
  1420.                 if fromthor then do
  1421.                     globalcfg.SYSTEM = cursys.BBSNAME
  1422.                     'GETCONFDATA "'globalcfg.SYSTEM'" "'globalcfg.CONFERENCE'" STEM 'confdata
  1423.                     if rc ~= 0 then call displayerror(30, 'SortMail', 'GETCONFDATA: 'BBSREAD.LASTERROR)
  1424.                     if confdata.MSGMARKED = 0 then signal cleanup
  1425.                     end
  1426.                 end
  1427.  
  1428.             when upper(subword(entry, 1, 1)) = "TRIGGER" then do
  1429.                 triggers = triggers + 1
  1430.                 trigger.triggers.cmdline = subword(entry, 2)
  1431.                 trigger.triggers.DELMSG = 0; trigger.triggers.DELUSER = 0; trigger.triggers.MATCHALL = 0; trigger.triggers.hitcount = 0; trigger.triggers.failcount = 0
  1432.                 'READARGS TEMPLATE "NAME/A,DELMSG/S,DELUSER/S,MATCHALL/S" STEM 'trigger.triggers' CMDLINE 'trigger.triggers.cmdline
  1433.                 if rc ~= 0 then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  1434.  
  1435.                 do
  1436.                     searches = 0; actions = 0
  1437.  
  1438.                     do until (upper(subentry) = 'ENDTRIGGER') | (eof(cf))
  1439.                         subentry = readln(cf); cfgline = cfgline + 1
  1440.  
  1441.                         select
  1442.                             when upper(subword(subentry, 1, 1)) = 'ACTION' then do
  1443.                                 actions = actions + 1
  1444.                                 trigger.triggers.action.actions.checkdupes = 0; trigger.triggers.action.actions.dontadd = 0; trigger.triggers.action.actions.nostats = 0; trigger.triggers.action.actions.header = 0; trigger.triggers.action.actions.append = 0; trigger.triggers.action.actions.nobin = 0; trigger.triggers.action.actions.hitcount = 0; trigger.triggers.action.actions.failcount = 0
  1445.                                 'READARGS TEMPLATE "TYPE/A,DESTSYS/K,DESTCONF/K,REPLYADDR/K,SCRIPTNAME/K,SCRIPTOPTS/K,FILENAME/K,DIRECTORY/K,SUBSTITUTE/K,WITH/K,HEADER/S,APPEND/S,NOBIN/S,CHECKDUPES/S,DONTADD/S,NOSTATS/S" STEM 'trigger.triggers.action.actions' CMDLINE 'subword(subentry, 2)
  1446.                                 trigger.triggers.action.actions.type = upper(trigger.triggers.action.actions.type)
  1447.                                 if rc ~= 0 then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  1448.                                 end
  1449.  
  1450.                             when upper(subword(subentry, 1, 1)) = 'SEARCH' then do
  1451.                                 searches = searches + 1
  1452.                                 'READARGS TEMPLATE "TYPE/A,CRITERIA/A,KEYWORD/K" STEM 'trigger.triggers.search.searches' CMDLINE 'subword(subentry, 2)
  1453.                                 trigger.triggers.search.searches.type = upper(trigger.triggers.search.searches.type)
  1454.                                 if rc ~= 0 then call displayerror(30, 'Read config', 'Error in 'cfgfile' line 'cfgline': 'BBSREAD.LASTERROR)
  1455.                                 end
  1456.  
  1457.                             when eof(cf) then call displayerror(30, 'Read config', 'Premature end of configuration file 'cfgfile)
  1458.  
  1459.                             when upper(subword(subentry, 1, 1)) = 'TRIGGER' then call displayerror(30, 'Read config', 'TRIGGER did not contain ENDTRIGGER in 'cfgfile' line 'cfgline)
  1460.  
  1461.                             otherwise nop
  1462.                             end
  1463.                         end
  1464.                     end
  1465.  
  1466.                 trigger.triggers.action.count = actions
  1467.                 trigger.triggers.search.count = searches
  1468.                 end
  1469.  
  1470.             otherwise nop
  1471.             end
  1472.  
  1473.         trigger.count = triggers
  1474.         end
  1475.     end
  1476. else do
  1477.     call displayerror(30, 'Read config', 'Couldn''t open ' || bbsdata.BBSPATH || cfgfile || ' for reading.')
  1478.     end
  1479.  
  1480. if trigger.count = 0 then signal cleanup
  1481.  
  1482. call close(cf)
  1483.  
  1484. return(0)
  1485.  
  1486.  
  1487.  /****************************************************************************
  1488. *********************** Display requester / type message **********************
  1489.  ****************************************************************************/
  1490.  
  1491. notify: interpret 'procedure expose 'globals
  1492.         parse arg message, choices
  1493.  
  1494. seperator = index(choices, '|')
  1495. if seperator > 0 then do
  1496.     firstchoice = substr(choices, 1, seperator - 1)
  1497.     secondchoice = substr(choices, seperator + 1)
  1498.     end
  1499.  
  1500. address(bbsread)
  1501. 'BUFMODE ENDCOPYBACK'
  1502.  
  1503. if ~fromthor then do
  1504.     message = substitute(message, '\n', ' ')
  1505.  
  1506.     if seperator > 0 then do
  1507.         say message
  1508.         options prompt firstchoice' or 'secondchoice': '
  1509.         parse pull choice
  1510.         options prompt
  1511.  
  1512.         select
  1513.             when upper(left(choice, 1)) = upper(left(firstchoice, 1)) then reqres = 1
  1514.             when upper(left(choice, 1)) = upper(left(secondchoice, 1)) then reqres = 0
  1515.             otherwise reqres = 0
  1516.             end
  1517.         say
  1518.         end
  1519.     else do
  1520.         say message
  1521.         say
  1522.         reqres = 0
  1523.         end
  1524.     end
  1525. else do
  1526.     message = substitute(message, '"', '*"')
  1527.     address(thorport)
  1528.     REQUESTNOTIFY '"'message'"' '"'choices'"'
  1529.     reqres = result
  1530.     end
  1531.  
  1532. address(bbsread)
  1533. 'BUFMODE COPYBACK'
  1534.  
  1535. return(reqres)
  1536.  
  1537.  
  1538.  /****************************************************************************
  1539. *********************** Display error and return or quit **********************
  1540.  ****************************************************************************/
  1541.  
  1542. displayerror: interpret 'procedure expose 'globals
  1543.               parse arg returned, caller, errmsg, msgno
  1544.  
  1545. if msgno ~= '' then errhead = caller' returned 'returned' on message #'msgno':'
  1546. else errhead = caller' returned 'returned' in line 'sigl':'
  1547.  
  1548. select
  1549.     when returned > 0 & returned < 6 then do
  1550.         if ~globalcfg.NOWARN then do
  1551.             call notify(errhead || '\n' || errmsg, 'Continue|Abort')
  1552.             if result = 0 then signal cleanup
  1553.             end
  1554.  
  1555.         errlogcount = errlogcount + 1; errlog.text.errlogcount = errhead
  1556.         errlogcount = errlogcount + 1; errlog.text.errlogcount = errmsg
  1557.         errlogcount = errlogcount + 1; errlog.text.errlogcount = ''
  1558.         end
  1559.  
  1560.     when returned > 5 & returned < 20 then do
  1561.         call notify(errhead || '\n' || errmsg, 'Continue|Abort')
  1562.         if result = 0 then signal cleanup
  1563.  
  1564.         errlogcount = errlogcount + 1; errlog.text.errlogcount = errhead
  1565.         errlogcount = errlogcount + 1; errlog.text.errlogcount = errmsg
  1566.         errlogcount = errlogcount + 1; errlog.text.errlogcount = ''
  1567.         end
  1568.  
  1569.     when returned > 19 then do
  1570.         call notify(errhead || '\n' || errmsg, 'Abort')
  1571.         signal cleanup
  1572.         end
  1573.  
  1574.     otherwise nop
  1575.     end
  1576.  
  1577. drop THOR.LASTERROR BBSREAD.LASTERROR
  1578.  
  1579. return(0)
  1580.  
  1581.  
  1582.  /****************************************************************************
  1583. ********************* Substitute a string within a string *********************
  1584. ******** Shamelessly ripped from Troels Walsted Hansen's UUDecode.thor ********
  1585.  ****************************************************************************/
  1586.  
  1587. substitute: interpret 'procedure expose 'globals
  1588.             parse arg str, org, new
  1589.  
  1590. lastfound = 0
  1591.  
  1592. found = index(str, org)
  1593.  
  1594. do while found > lastfound
  1595.     secondpart = substr(str, found + length(org))
  1596.     firstpart = substr(str, 1, length(str) - length(substr(str, found)))
  1597.     str = firstpart || new || secondpart
  1598.     lastfound = found + length(new)
  1599.     found = index(str, org, lastfound)
  1600.     end
  1601.  
  1602. return(str)
  1603.  
  1604.  
  1605.  /****************************************************************************
  1606. ******************************** Split digests ********************************
  1607.  ****************************************************************************/
  1608.  
  1609. splitdigest: interpret 'procedure expose 'globals
  1610.              parse arg msgno, destconf, repaddr, tosys, mailconf
  1611.  
  1612. /*
  1613. ** Read message's header and text stems
  1614. */
  1615.  
  1616. if textread = 0 then do
  1617.     'READBRMESSAGE "'globalcfg.SYSTEM'" "'globalcfg.CONFERENCE'" 'msgno' TEXTSTEM 'text
  1618.     if rc ~= 0 then do
  1619.         call displayerror(10, 'Split digest', 'READBRMESSAGE, text stem: 'BBSREAD.LASTERROR, msgno)
  1620.         return(10)
  1621.         end
  1622.     end
  1623.  
  1624. if symbol('text.TEXT.COUNT') = 'VAR' & text.TEXT.COUNT > 0 & symbol('text.PART.COUNT') = 'VAR' & text.PART.COUNT > 0 then do
  1625.     call displayerror(5, 'Split digest', 'Message contains both a text body and text parts.', msgno)
  1626.     return(5)
  1627.     end
  1628.  
  1629. if symbol('text.PART.COUNT') = 'VAR' & text.PART.COUNT > 0 then do
  1630.     returned = 0
  1631.     do i = 1 to text.PART.COUNT
  1632.         if (symbol('text.PART.'i'.TEXT.COUNT') = 'VAR' | symbol('text.PART.'i'.COMMENT.COUNT') = 'VAR' | symbol('text.PART.'i'.BINARY') = 'VAR') & (returned ~= 5) then do
  1633.             call displayerror(5, 'Split digest', 'MIME digest contains a non-text part.', msgno)
  1634.             returned = 5
  1635.             end
  1636.         if symbol('text.PART.'i'.MSG.FROMNAME')        ~= 'VAR' then text.PART.i.MSG.fromname          = head.FROMNAME
  1637.         if symbol('text.PART.'i'.MSG.FROMADDR')        ~= 'VAR' then text.PART.i.MSG.fromaddr          = head.FROMADDR
  1638.         if symbol('text.PART.'i'.MSG.TONAME')          ~= 'VAR' then text.PART.i.MSG.toname            = head.TONAME
  1639.         if symbol('text.PART.'i'.MSG.TOADDR')          ~= 'VAR' then text.PART.i.MSG.toaddr            = head.TOADDR
  1640.         /*    if symbol('text.PART.'i'.MSG.MSGID')           ~= 'VAR' then text.PART.i.MSG.msgid             = head.MSGID*/
  1641.         if symbol('text.PART.'i'.MSG.REFID')           ~= 'VAR' then text.PART.i.MSG.refid             = head.REFID
  1642.         if symbol('text.PART.'i'.MSG.CREATIONDATE')    ~= 'VAR' then text.PART.i.MSG.creationdate      = head.CREATIONDATE
  1643.         if symbol('text.PART.'i'.MSG.CREATIONDATETXT') ~= 'VAR' then text.PART.i.MSG.creationdatetxt   = head.CREATIONDATETXT
  1644.         if symbol('text.PART.'i'.MSG.SUBJECT')         ~= 'VAR' then text.PART.i.MSG.subject           = head.SUBJECT
  1645.  
  1646.         if (repaddr = '') | (right(repaddr, 9) = 'REPLYADDR') then do
  1647.             if symbol('text.replyaddr') = 'VAR' then do
  1648.                 text.PART.i.MSG.replyaddr = text.replyaddr
  1649.                 if symbol('text.replyname') = 'VAR' then text.PART.i.MSG.replyname = text.replyname
  1650.                 end
  1651.             else do
  1652.                 text.PART.i.MSG.replyaddr = head.fromaddr
  1653.                 if symbol('head.fromname') = 'VAR' then text.PART.i.MSG.replyname = head.fromname
  1654.                 end
  1655.             end
  1656.         else text.PART.i.MSG.replyaddr = repaddr
  1657.  
  1658.         text.PART.i.MSG.replyconf = mailconf
  1659.  
  1660.         if (tosys = '') | (right(tosys, 7) = 'DESTSYS') then call writemessage(SYSTEM '"'globalcfg.SYSTEM'"' CONFERENCE '"'destconf'"' MSGSTEM value(text.PART.'i'.MSG))
  1661.         else call writemessage(SYSTEM '"'tosys'"' CONFERENCE '"'destconf'"' MSGSTEM value(text.PART.'i'.MSG))
  1662.  
  1663.         if result ~= 0 then do
  1664.             if (returned > 0) & (returned > result) then return(returned)
  1665.             else return(result)
  1666.             end
  1667.         end
  1668.     end
  1669. else do
  1670.     line = 1; parsed = 0; drop endsubmsg
  1671.  
  1672.     do forever
  1673.         newmsg.text.count = 0
  1674.  
  1675.         fromline = 0; subjline = 0; dateline = 0; msgoffset = 0
  1676.  
  1677.         do until (text.text.line = '') & (fromline ~= 0)
  1678.             select
  1679.                 when upper(subword(text.text.line, 1, 1)) = "FROM:" then fromline = line
  1680.                 when upper(subword(text.text.line, 1, 1)) = "SUBJECT:" then subjline = line
  1681.                 when upper(subword(text.text.line, 1, 1)) = "DATE:" then dateline = line
  1682.                 when msgoffset > 100 then do
  1683.                     call displayerror(5, 'Parse digest', 'Couldn''t find sub-message, no from: line.', msgno)
  1684.                     return(5)
  1685.                     end
  1686.                 when line > text.text.count then do
  1687.                     if ~parsed then do
  1688.                         call displayerror(5, 'Parse digest', 'Unexpected end of message.', msgno)
  1689.                         return(5)
  1690.                         end
  1691.                     else return(0)
  1692.                     end
  1693.                 otherwise nop
  1694.                 end
  1695.  
  1696.             line = line + 1; msgoffset = msgoffset + 1
  1697.             end
  1698.  
  1699.         /* Pick up date line and skip blank lines */
  1700.  
  1701.         do forever
  1702.             if line = text.text.count then do
  1703.                 call displayerror(5, 'Parse digest', 'Failed to find start of first submessage''s text body.', msgno)
  1704.                 return(5)
  1705.                 end
  1706.             if upper(subword(text.text.line, 1, 1)) = "DATE:" then dateline = line
  1707.             else if text.text.line ~= '' then break
  1708.             line = line + 1
  1709.             end
  1710.  
  1711.         /* Search for 'End of message' line or the end of the digest */
  1712.  
  1713.         newmsg.text.count = 0; msgline = 0; foundend = 0
  1714.         do forever
  1715.             notthisone = 0
  1716.  
  1717.             if line = text.text.count then do
  1718.                 call displayerror(5, 'Parse digest', 'Immature end of message.', msgno)
  1719.                 return(5)
  1720.                 end
  1721.  
  1722.             if (symbol('endsubmsg') ~= 'VAR') & (upper(subword(text.text.line, 1, 1)) = "FROM:") then do
  1723.                 call displayerror(5, 'Parse digest', 'End of submessage not detected.', msgno)
  1724.                 return(5)
  1725.                 end
  1726.  
  1727.             if symbol('endsubmsg') ~= 'VAR' then do
  1728.                 if (left(text.text.line, 1) = '-') & (length(text.text.line) > 29) & (length(text.text.line) = length(compress(text.text.line))) & (text.text.line = copies(left(text.text.line, 1), length(text.text.line))) then do
  1729.                     templine = line + 1
  1730.                     do until (foundend) | (notthisone) | (templine = text.text.count)
  1731.                         if (text.text.templine ~= '') then do
  1732.                             if (upper(subword(text.text.templine, 1, 1)) = "FROM:") | (upper(subword(text.text.templine, 1, 1)) = "DATE:") | (upper(subword(text.text.templine, 1, 1)) = "SUBJECT:") | (upper(left(text.text.templine, 9)) = 'MESSAGE #') then do
  1733.                                 endsubmsg = text.text.line; foundend = 1
  1734.                                 end
  1735.                             else if ((templine + 7) <= text.text.COUNT) then notthisone = 1; else foundend = 1
  1736.                             end
  1737.                         templine = templine + 1
  1738.                         end
  1739.                     end
  1740.                 end
  1741.             else if compare(endsubmsg, text.text.line) = 0 then foundend = 1
  1742.  
  1743.             if foundend then break
  1744.  
  1745.             msgline = msgline + 1; newmsg.text.msgline = text.text.line
  1746.             newmsg.text.count = newmsg.text.count + 1
  1747.             line = line + 1
  1748.             end
  1749.  
  1750.         /* Some magic to find name, address and subject */
  1751.  
  1752.         newmsg.subject = "<no subject>"
  1753.         newmsg.fromname = "Unknown"
  1754.         newmsg.fromaddr = "<no address>"
  1755.  
  1756.         from = strip(substr(text.text.fromline, 6))
  1757.         from = translate(from, '<>', '()')
  1758.         i = pos("<", from)
  1759.  
  1760.         if (i ~= 0) & (pos(">", from) > 0) then do
  1761.             checkaddr = strip(substr(from, i, pos('>', from) - i), B, ' <>"')
  1762.             if pos("@", checkaddr) = 0 then do
  1763.                 newmsg.fromname = checkaddr
  1764.                 newmsg.fromaddr = strip(delstr(from, i, pos('>', from) - i), B, ' >')
  1765.                 end
  1766.             else do
  1767.                 newmsg.fromaddr = checkaddr
  1768.                 newmsg.fromname = strip(delstr(from, i, pos('>', from) - i), B, ' ">')
  1769.                 end
  1770.             end
  1771.         else do
  1772.             if pos("@", from) = 0 then do
  1773.                 newmsg.fromname = strip(from, B, ' <>"')
  1774.                 end
  1775.             else do
  1776.                 newmsg.fromaddr = strip(from, B, ' <>"')
  1777.                 end
  1778.             end
  1779.  
  1780.         if subjline ~= 0 then newmsg.subject = strip(subword(text.text.subjline, 2))
  1781.         if dateline ~= 0 then newmsg.creationdatetxt = strip(subword(text.text.dateline, 2))
  1782.  
  1783.         /* Removed msgid copying to avoid multiple messages with the same message id
  1784.         if symbol('head.MSGID')           ~= 'VAR' then newmsg.msgid             = head.MSGID
  1785.         */
  1786.  
  1787.         /* Find correct reply address */
  1788.  
  1789.         if (repaddr = '') | (right(repaddr, 9) = 'REPLYADDR') then do
  1790.             if symbol('text.replyaddr') = 'VAR' then do
  1791.                 newmsg.replyaddr = text.replyaddr
  1792.                 if symbol('text.replyname') = 'VAR' then newmsg.replyname = text.replyname
  1793.                 end
  1794.             else do
  1795.                 newmsg.replyaddr = head.fromaddr
  1796.                 if symbol('head.fromname') = 'VAR' then newmsg.replyname = head.fromname
  1797.                 end
  1798.             end
  1799.         else newmsg.replyaddr = repaddr
  1800.  
  1801.         newmsg.replyconf = mailconf
  1802.  
  1803.         if (tosys = '') | (right(tosys, 7) = 'DESTSYS') then call writemessage(SYSTEM '"'globalcfg.SYSTEM'"' CONFERENCE '"'destconf'"' MSGSTEM newmsg)
  1804.         else call writemessage(SYSTEM '"'tosys'"' CONFERENCE '"'destconf'"' MSGSTEM newmsg)
  1805.  
  1806.         if result ~= 0 then return(result)
  1807.         drop newmsg.; parsed = 1
  1808.         end
  1809.     end
  1810.  
  1811. return(0)
  1812.  
  1813.  
  1814.  /****************************************************************************
  1815. ****************************** Check Thor version *****************************
  1816.  ****************************************************************************/
  1817.  
  1818. getver: interpret 'procedure expose 'globals
  1819.  
  1820. address(thorport)
  1821. 'VERSION STEM 'ver
  1822.  
  1823. fullver = subword(ver.THOR, 1, 1)
  1824. ver.thorver = substr(fullver, 1, index(fullver, '.') - 1)
  1825. ver.thorrev = compress(substr(fullver, index(fullver, '.') + 1), 'ß')
  1826. ver.thorrev = ver.thorrev 
  1827.  
  1828. /* Circumvent a bug in Thor 2.31 that appends garbage to ver.THOR */
  1829.  
  1830. if left(ver.thorrev, 2) = 31 then ver.thorrev = 31
  1831.  
  1832. return(0)
  1833.